﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using log4net;
using System.Diagnostics;
using Microsoft.SharePoint.Utilities;
using System.IO;
using System.Reflection;

namespace Validation.sp2010.Helper
{
    public static class Log
    {
        private const string LogName = "WF.Validation"; //used to set the Event Source for the EventLog (in future to describe the area in the ULS Log)

        private static ILog logger;
        public static ILog Logger
        {
            get
            {
                if (logger == null)
                {
                    try
                    {
                        string logConfigPath = Path.Combine(SPUtility.GetVersionedGenericSetupPath("CONFIG", 15), "WorkflowValidation.config");

                        log4net.Config.XmlConfigurator.Configure(new Uri(logConfigPath));
                    }
                    catch
                    { /*this is deliberately left empty*/ }

                    logger = LogManager.GetLogger(Assembly.GetCallingAssembly(), typeof(Log));
                }

                return logger;
            }
        }

        /// <summary>
        /// Logs the entry into a Method
        /// </summary>
        public static void Entry()
        {
            ConfigureApplicationNameforLogging();
            Logger.Debug("Entered: " + GetMethodInformation());
        }

        /// <summary>
        /// Logs the exit out of a Method
        /// </summary>
        public static void Exit()
        {
            ConfigureApplicationNameforLogging();
            Logger.Debug("Exiting: " + GetMethodInformation());
        }

        /// <summary>
        /// Logs a Debug message.
        /// </summary>
        /// <param name="message">The message.</param>
        public static void Debug(object message)
        {
            ConfigureApplicationNameforLogging();
            Logger.Debug(message + GetMethodInformation());
        }

        /// <summary>
        /// Logs a Debug message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The ex.</param>
        public static void Debug(object message, Exception ex)
        {
            ConfigureApplicationNameforLogging();
            Logger.Debug(message, ex);
        }

        /// <summary>
        /// Logs an Info message.
        /// </summary>
        /// <param name="message">The message.</param>        
        public static void Info(object message)
        {
            ConfigureApplicationNameforLogging();
            Logger.Info(message + GetMethodInformation());
        }

        /// <summary>
        /// Logs an Info message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The ex.</param>
        public static void Info(object message, Exception ex)
        {
            ConfigureApplicationNameforLogging();
            Logger.Info(message, ex);
        }

        /// <summary>
        /// Logs a warning message.
        /// </summary>
        /// <param name="message">The message.</param>
        public static void Warning(object message)
        {
            ConfigureApplicationNameforLogging();
            Logger.Warn(message + GetMethodInformation());
        }

        /// <summary>
        /// Logs a warning message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The ex.</param>
        public static void Warning(object message, Exception ex)
        {
            ConfigureApplicationNameforLogging();
            Logger.Warn(message, ex);
        }

        /// <summary>
        /// Logs an Error message.
        /// </summary>
        /// <param name="message">The message.</param>
        public static void Error(object message)
        {
            ConfigureApplicationNameforLogging();
            Logger.Error(message + GetMethodInformation());
        }

        /// <summary>
        /// Logs an Error message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The ex.</param>
        public static void Error(object message, Exception ex)
        {
            ConfigureApplicationNameforLogging();
            Logger.Error(message, ex);
        }


        /// <summary>
        /// Logs a Fatal message.
        /// </summary>
        /// <param name="message">The message.</param>
        public static void Fatal(object message)
        {
            ConfigureApplicationNameforLogging();
            Logger.Fatal(message + GetMethodInformation());
        }

        /// <summary>
        /// Logs a Fatal message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The ex.</param>
        public static void Fatal(object message, Exception ex)
        {
            ConfigureApplicationNameforLogging();
            Logger.Fatal(message, ex);
        }

        /// <summary>
        /// Configures the name of the application for Logging.
        /// </summary>
        private static void ConfigureApplicationNameforLogging()
        {
            try
            {
                log4net.Appender.EventLogAppender appender = (log4net.Appender.EventLogAppender)Logger.Logger.Repository.GetAppenders().FirstOrDefault(a => a.Name == "EventLogAppender");
                if (appender != null)
                {
                    appender.LogName = Log.LogName;
                    appender.ApplicationName = GetAssemblyNameforEventLogSource();
                    appender.ActivateOptions();
                }
            }
            catch
            { /*this is deliberately left empty*/ }
        }

        /// <summary>
        /// Gets the assembly name for event log source.
        /// </summary>
        /// <returns></returns>
        private static string GetAssemblyNameforEventLogSource()
        {
            StackTrace stackTrace = new StackTrace();           // get call stack
            StackFrame[] stackFrames = stackTrace.GetFrames();  // get method calls (frames)

            var assembly = stackFrames[0].GetMethod().DeclaringType.Assembly;

            string currentAssemblyName = assembly.GetName().Name.ToString();

            foreach (var stackFrame in stackFrames)
            {
                if (!stackFrame.GetMethod().DeclaringType.Assembly.GetName().Name.Equals(currentAssemblyName))
                {
                    return stackFrame.GetMethod().DeclaringType.Assembly.GetName().Name.ToString();
                }
            }

            return currentAssemblyName;
        }

        /// <summary>
        /// Gets the method information for Logging.
        /// </summary>
        /// <returns></returns>
        private static string GetMethodInformation()
        {
            StackTrace stackTrace = new StackTrace();           // get call stack
            StackFrame[] stackFrames = stackTrace.GetFrames();  // get method calls (frames)

            var assembly = stackFrames[0].GetMethod().DeclaringType.Assembly;

            string currentAssemblyName = assembly.GetName().Name.ToString();

            foreach (var stackFrame in stackFrames)
            {
                if (!stackFrame.GetMethod().DeclaringType.Assembly.GetName().Name.Equals(currentAssemblyName))
                {
                    return " " + stackFrame.GetMethod().DeclaringType.Name + "." + stackFrame.GetMethod().Name + "()";
                }
            }

            return " " + stackFrames[0].GetMethod().DeclaringType.Name + "." + stackFrames[0].GetMethod().Name + "()";
        }
    }
}
